home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / SCSI Samples 1.0 / SCSI Simple Sample 06⁄15 ƒ / Src / OriginalSCSI.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-16  |  17.1 KB  |  470 lines  |  [TEXT/KAHL]

  1. /*                                OriginalSCSI.c                                    */
  2. /*
  3.  * OriginalSCSI.c
  4.  * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
  5.  *
  6.  * Talk to the Macintosh SCSI Handler using the "old" interface as defined in
  7.  * Inside Mac IV. This is a synchronous call that executes a single SCSI Command
  8.  * on a device. It does not support multiple busses or logical units.
  9.  *
  10.  * Calling Sequence:
  11.  *        OSErr                OriginalSCSI(
  12.  *                short                    targetID,
  13.  *                const SCSI_CommandPtr    scsiCommand,
  14.  *                unsigned short            cmdBlockLength,
  15.  *                Boolean                    writeToDevice,
  16.  *                Ptr                        bufferPtr,
  17.  *                unsigned long            transferSize,
  18.  *                unsigned long            transferQuantum,
  19.  *                unsigned long            completionTimeout,
  20.  *                unsigned short            *stsBytePtr,
  21.  *                unsigned long            *actualTransferCount
  22.  *            );
  23.  * The parameters have the following meaning:
  24.  *
  25.  *    targetID            The SCSI Bus ID of the target (0 .. 6). Note that this
  26.  *                        function can only access LUN zero.
  27.  *    scsiCommand            The SCSI Command Block (6, 10, or 12 bytes). The command
  28.  *                        block length will be determined by examining the command
  29.  *                        parameter. This is standardized for all but
  30.  *                        "vendor-specific" commands.
  31.  *    writeToDevice        TRUE if this command writes to the device. FALSE if this
  32.  *                        command reads from the device or does not require a data
  33.  *                        phase.
  34.  *    bufferPtr            The user data buffer for Read/Write commands. It should be
  35.  *                        NULL if a data transfer phase is not used for this command.
  36.  *                        (e.g. for Test Unit Ready).
  37.  *    transferSize        The total number of bytes to transfer to or from the device.
  38.  *    transferQuantum        This is needed to configure the transfer information block
  39.  *                        (TIB). The following values are appropriate:
  40.  *                        -- Set to zero for a one-shot blind transfer.
  41.  *                            ActualTransferCount is not correctly returned.
  42.  *                        -- Set to one if a polled transfer is needed. This is
  43.  *                            useful for Request Sense or other management requests,
  44.  *                            especially requests with a variable-length record. On
  45.  *                            return, the actual number of bytes that were transferred
  46.  *                            will be in the actualTransferCount variable.
  47.  *                        -- Set to the transferSize value for a normal, "Blind"
  48.  *                            transfer. This is the normal case for data requests.
  49.  *                            ActualTransferCount will equal transferSize on success,
  50.  *                            but does not correctly return the actual count on phase
  51.  *                            errors. This, however, can be recovered from the Request
  52.  *                            Sense record.
  53.  *                        -- Set to a sub-multiple (such as a block length) for
  54.  *                            requests that need re-synchronization between sectors.
  55.  *                        -- Other values will likely result in errors.
  56.  *    completionTimeout    The timeout (in Ticks) for the command. This should be short
  57.  *                        for disks, but must be long for tape devices and some setup
  58.  *                        requests, such as Mode Select.
  59.  *    stsBytePtr            This short is set to the byte returned in the device's
  60.  *                        Status Phase.
  61.  *    actualTransferCount    This will be set to the number of "cycles" through the TIB
  62.  *                        loop times the transferCount. This should equal the number
  63.  *                        of bytes transferred if transferCount is set to one.
  64.  *                        (Ignored if NULL.)
  65.  * Return codes:
  66.  *    noErr            normal
  67.  *    scCommErr        no such device (selection error)
  68.  *    scPhaseErr        user data buffer was the wrong size for the transfer.
  69.  *                    Look at the status byte to see if this is a problem: 
  70.  *                    you may merely have given a large buffer size to
  71.  *                    a variable-length request, such as Device Inquiry.
  72.  *    sc...            other SCSI error.
  73.  *    statusErr        Device returned "Check condition." The caller should
  74.  *                    issue a Request Sense SCSI Command to this device.
  75.  *    controlErr        Device returned "Busy"
  76.  *    ioErr            Other (serious) device status. The caller should
  77.  *                    examine the Status and Message bytes to determine
  78.  *                    the problem.
  79.  *    paramErr        Could not determine the command length.
  80.  */
  81. #include <scsi.h>
  82. #include <Errors.h>
  83. #include <GestaltEqu.h>
  84. #include <Memory.h>
  85. #include <Events.h>
  86. #include "MacSCSICommand.h"
  87. #ifndef TRUE
  88. #define FALSE        0
  89. #define TRUE        1
  90. #endif
  91.  
  92. /*
  93.  * Execute a SCSI command using the original (Inside Mac IV) SCSI Manager.
  94.  * Return codes:
  95.  *
  96.  *    noErr            normal
  97.  *    paramErr        could not determine command length from command
  98.  *    scCommErr        could not select this device or bus busy
  99.  *    sc...            other scsi error
  100.  *    statusErr        Device returned "Check condition"
  101.  *    controlErr        Device returned "Busy" (Note: device error)
  102.  *    ioErr            Other (serious) device status -- bug.
  103.  */
  104. OSErr                        OriginalSCSI(
  105.         short                    targetID,            /* Device ID on this bus    */
  106.         const SCSI_CommandPtr    scsiCommand,        /* The actual scsi command    */
  107.         unsigned short            cmdBlockLength,        /* -> Length of CDB            */
  108.         Boolean                    writeToDevice,        /* TRUE to write            */
  109.         Ptr                        bufferPtr,            /* -> user data buffer        */
  110.         unsigned long            transferSize,        /* How much to transfer        */
  111.         unsigned long            transferQuantum,    /* TIB setup parameter        */
  112.         unsigned long            completionTimeout,    /* Ticks to wait            */
  113.         unsigned short            *stsBytePtr,        /* <- status phase byte        */
  114.         unsigned long            *actualTransferCount
  115.     );
  116.  
  117. /*
  118.  * SCSI command status (from status phase)
  119.  */
  120. #define     kScsiStatusGood            0x00            /* Normal completion        */
  121. #define     kScsiStatusCheckCondition    0x02            /* Need GetExtendedStatus    */
  122. #define     kScsiStatusConditionMet    0x04            /* For Compare Command?        */
  123. #define     kScsiStatusBusy            0x08            /* Device busy (self-test?)    */
  124. #define     kScsiStatusIntermediate    0x10            /* Intermediate status        */
  125. #define     kScsiStatusResConflict        0x18            /* Reservation conflict        */
  126. #define     kScsiStatusQueueFull        0x28            /* Target can't do command    */
  127. #define     kScsiStatusReservedMask    0x3e            /* Vendor specific?            */
  128.  
  129. /*
  130.  * This is the maximum number of times we try to grab the SCSI Bus
  131.  */
  132. #define kMaxSCSIRetries                40                /* 10 seconds, 4 times/sec    */
  133. /*
  134.  * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
  135.  * if either the BSY or SEL bit is set).
  136.  */
  137. #ifndef kScsiStatBSY
  138. #define kScsiStatBSY                (1 << 6)
  139. #endif
  140. #ifndef kScsiStatSEL
  141. #define kScsiStatSEL                (1 << 1)
  142. #endif
  143. #define ScsiBusBusy()        ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
  144.  
  145. static void        NextFunction(void);        /* Dummy function for OriginalSCSI size    */
  146. static Boolean    IsVirtualMemoryRunning(void);
  147. /*
  148.  * These are bitmasks for the vmHoldMask variable. A bit is set if its associated
  149.  * memory element has been held in protected (non-paged) memory.
  150.  */
  151. #define kHoldFunction            0x0001                /* AsyncSCSI function code    */
  152. #define kHoldStack                0x0002                /* Local variables            */
  153. #define kHoldUserBuffer            0x0004                /* User data buffer, if any    */
  154. #define kHoldCommandBlock        0x0008                /* SCSI Command Data Block    */
  155.  
  156. /*
  157.  * Execute a SCSI command.
  158.  * Returns the final status as noted above.
  159.  */
  160. OSErr
  161. OriginalSCSI(
  162.         short                    targetID,            /* SCSI device on this bus    */
  163.         const SCSI_CommandPtr    scsiCommand,        /* The actual scsi command    */
  164.         unsigned short            cmdBlockLength,        /* -> Length of CDB            */
  165.         Boolean                    writeToDevice,        /* TRUE to write            */
  166.         Ptr                        bufferPtr,            /* -> user data buffer        */
  167.         unsigned long            transferSize,        /* How much to transfer        */
  168.         unsigned long            transferQuantum,    /* TIB setup parameter        */
  169.         unsigned long            completionTimeout,    /* Ticks to wait            */
  170.         unsigned short            *stsBytePtr,        /* <- status phase byte        */
  171.         unsigned long            *actualTransferCount
  172.     )
  173. {
  174.         OSErr                    status;                /* Final status                */
  175.         OSErr                    completionStatus;    /* Status from ScsiComplete    */
  176.         short                    totalTries;            /* Get/Select retries        */
  177.         short                    getTries;            /* Get retries                */
  178.         short                    iCount;                /* Bus free counter            */
  179.         unsigned long            watchdog;            /* Timeout after this        */
  180.         unsigned long            myTransferCount;    /* Gets TIB loop counter    */
  181.         /*
  182.          * The TIB has the following format:
  183.          *    [0]    scInc    user buffer            transferQuantum or transferSize
  184.          *    [1] scAdd    &theTransferCount    1
  185.          *    [2] scLoop    -> tib[0]            transferSize / transferQuantum
  186.          *    [3] scStop
  187.          * The intent of this is to return, in actualTransferCount, the number
  188.          * of times we cycled through the tib[] loop. This will be the actual
  189.          * transfer count if transferQuantum equals one, or the number of
  190.          * "blocks" if transferQuantum is the length of one sector.
  191.          */
  192.         SCSIInstr                tib[4];                /* Current TIB                */
  193.         short                    messageByte;        /* For Command Complete 
  194.         /*
  195.          * The following parameters are used to manage virtual memory. The code
  196.          * is taken from the DTS SCSI Sample Driver.
  197.          */
  198.         unsigned short            vmHoldMask;
  199.         unsigned long            vmFunctionSize;
  200.         char                    *vmProtectedStackBase;    /* Last local variable    */
  201. /*
  202.  * These values are used to compute the size of the stack that we must hold in
  203.  * protected (non-virtual) memory. kSCSIManagerStackEstimate is an estimate.
  204.  */
  205. #define kSCSILocalVariableSize    ( \
  206.         (unsigned long) (((Ptr) &status) - ((Ptr) &vmProtectedStackBase))    \
  207.     )
  208. #define kSCSIManagerStackEstimate 512
  209. #define kSCSIProtectedStackSize (kSCSIManagerStackEstimate + kSCSILocalVariableSize)
  210.  
  211.         status = noErr;
  212.         vmHoldMask = 0;
  213.         /*
  214.          * If there is a data transfer, setup the tib.
  215.          */
  216.         myTransferCount = 0;
  217.         if (transferQuantum == 0)
  218.             transferQuantum = transferSize;
  219.         if (bufferPtr != NULL) {
  220.             tib[0].scOpcode = scInc;
  221.             tib[0].scParam1 = (unsigned long) bufferPtr;
  222.             tib[0].scParam2 = transferQuantum;
  223.             tib[1].scOpcode = scAdd;
  224.             tib[1].scParam1 = (unsigned long) &myTransferCount;
  225.             tib[1].scParam2 = transferQuantum;
  226.             tib[2].scOpcode = scLoop;
  227.             tib[2].scParam1 = (-2 * sizeof (SCSIInstr));
  228.             tib[2].scParam2 = transferSize / tib[0].scParam2;
  229.             tib[3].scOpcode = scStop;
  230.             tib[3].scParam1 = 0;
  231.             tib[3].scParam2 = 0;
  232.         }
  233.         if (IsVirtualMemoryRunning()) {
  234.  
  235.             /*
  236.              * Virtual memory is active. Lock all of the memory segments that we
  237.              * need in "real" memory (i.e. non-paged pool) for the duration of the
  238.              * call. Since  we need to back out of VM holds if there are errors,
  239.              * we'll use bits in vmHoldMask to record the status of our attempts.
  240.              *
  241.              * Note: in a real application or driver, the user buffers should be
  242.              * held outside of the SCSI Manager code:
  243.              *        HoldMemory(data buffer);
  244.              *        HoldMemory(autosense buffer);
  245.              *        status = CallSCSIManager(...);
  246.              *        UnholdMemory(...);
  247.              *
  248.              * First, hold the MacSCSI function. It starts at AsyncSCSI and
  249.              * extends to the start of the next function. This is marked by a
  250.              * dummy function. The left-margin comments indicate the value
  251.              * of vmHoldCount if the indicated HoldMemory succeeded. This is not
  252.              * needed for drivers.
  253.              */
  254.             vmFunctionSize =
  255.                 (unsigned long) NextFunction - (unsigned long) OriginalSCSI;
  256.             status = HoldMemory(OriginalSCSI, vmFunctionSize);
  257.             if (status == noErr)
  258.                 vmHoldMask |= kHoldFunction;
  259.             if (status == noErr) {
  260.                 /*
  261.                  * Hold a chunk of stack space to call the SCSI Manager and to
  262.                  * protect our local variables. This is always needed, as drivers
  263.                  * can be called from application contexts.
  264.                  */
  265.                 vmProtectedStackBase =
  266.                     (char *) &vmProtectedStackBase - kSCSIManagerStackEstimate;
  267.                 status = HoldMemory(vmProtectedStackBase, kSCSIProtectedStackSize);
  268.                 if (status == noErr)
  269.                     vmHoldMask |= kHoldStack;
  270.             }
  271.             if (status == noErr) {
  272.                 /*
  273.                  * Lock down the command block. In this sample, we allocated
  274.                  * the command block in the application heap. A driver would
  275.                  * typically allocate it in the System Heap and, hence, would
  276.                  * not require this call.
  277.                  */
  278.                 status = HoldMemory((Ptr) scsiCommand, cmdBlockLength);
  279.                 if (status == noErr)
  280.                     vmHoldMask |= kHoldCommandBlock;
  281.             }        
  282.             if (status == noErr && bufferPtr != NULL) {
  283.                 /*
  284.                  * Lock down the user buffer, if any. In a real-world application
  285.                  * or driver, this would be done before calling the SCSI interface.
  286.                  */
  287.                 status = HoldMemory(bufferPtr, transferSize);
  288.                 if (status != noErr)
  289.                     vmHoldMask |= kHoldStack;
  290.             }
  291.             if (status != noErr)
  292.                 goto exit;
  293.         }
  294.         /*
  295.          * Arbitrate for the scsi bus.  This will fail if some other device is
  296.          * accessing the bus at this time (which is unlikely).
  297.          *
  298.          *** Do not set breakpoints or call any functions that may require device
  299.          *** I/O (such as display code that accesses font resources between
  300.          *** SCSIGet and SCSIComplete,
  301.          *
  302.          */
  303.         for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
  304.             for (getTries = 0; getTries < 4; getTries++) {
  305.                 /*
  306.                  * Wait for the bus to go free.
  307.                  */
  308.                 watchdog = TickCount() + 300;        /* 5 second timeout            */
  309.                 while (ScsiBusBusy()) {
  310.                     if (TickCount() > watchdog) {
  311.                         status = scArbNBErr;
  312.                         goto exit;
  313.                     }
  314.                 }
  315.                 /*
  316.                  * The bus is free, try to grab it
  317.                  */
  318.                 for (iCount = 0; iCount < 4; iCount++) {
  319.                     if ((status = SCSIGet()) == noErr)
  320.                         break;
  321.                 }
  322.                 if (status == noErr)
  323.                     break;                            /* Success: we have the bus */
  324.                 /*
  325.                  * The bus became busy again. Try to wait for it to go free.
  326.                  */
  327.                 for (iCount = 0; iCount < 100 && ScsiBusBusy(); iCount++)
  328.                     ;
  329.             } /* The getTries loop */
  330.             if (status != noErr) {
  331.                 /*
  332.                  * The SCSI Manager thinks the bus is not busy and not selected,
  333.                  * but "someone" has set its internal semaphore that signals
  334.                  * that the SCSI Manager itself is busy. The application will have
  335.                  * to handle this problem. (We tried getTries * 4 times).
  336.                  */
  337.                 goto exit;
  338.             }
  339.             /*
  340.              * We now own the SCSI bus. Try to select the device.
  341.              */
  342.             if ((status = SCSISelect(targetID)) != noErr)
  343.                 goto exit;
  344.             /*
  345.              * From this point on, we must exit through SCSIComplete() even if an
  346.              * error is detected. Send a command to the selected device. There are
  347.              * several failure modes, including an illegal command (such as a
  348.              * write to a read-only device). If the command failed because of
  349.              * "device busy", we will try it again.
  350.              */
  351.             status = SCSICmd((Ptr) scsiCommand, cmdBlockLength);
  352.             if (status == noErr && bufferPtr != NULL) {
  353.                 /*
  354.                  * This command requires a data transfer.
  355.                  */
  356.                 if (writeToDevice) {
  357.                     if (transferQuantum == 1)
  358.                         status = SCSIWrite((Ptr) tib);
  359.                     else {
  360.                         status = SCSIWBlind((Ptr) tib);
  361.                     }
  362.                 }
  363.                 else {
  364.                     if (transferQuantum == 1)
  365.                         status = SCSIRead((Ptr) tib);
  366.                     else {
  367.                         status = SCSIRBlind((Ptr) tib);
  368.                     }
  369.                 }
  370.             }
  371. finish:
  372.             /*
  373.              * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
  374.              * returning the status and command-completion message bytes..
  375.              */
  376.             completionStatus = SCSIComplete(
  377.                         (short *) stsBytePtr,
  378.                         &messageByte,
  379.                         completionTimeout
  380.                     );
  381.             /*
  382.              * If we have an error here, return as the "final" status.
  383.              * 
  384.              */
  385.             if (completionStatus != noErr)
  386.                 status = completionStatus;
  387.             else {
  388.                 /*
  389.                  * ScsiComplete is happy. If the device is busy, Pause for 1/4
  390.                  * second and try again.
  391.                  */
  392.                 if (*stsBytePtr == kScsiStatusBusy) {
  393.                     watchdog = TickCount() + 15;
  394.                     while (TickCount() < watchdog)
  395.                         ;
  396.                     continue;                /* Do next totalTries attempt        */
  397.                 }
  398.             }
  399.             /*
  400.              * This is the normal exit (success) or final failure exit.
  401.              */
  402.             break;
  403.         } /* totalTries loop */
  404. exit:
  405.         /*
  406.          * If we held memory, unhold it now.  We ignore UnholdMemory errors:
  407.          * there isn't much we can do about them. Note that this must be
  408.          * done by driver or asynchronous completion routines.
  409.          */
  410.         if ((vmHoldMask & kHoldUserBuffer) != 0)
  411.             (void) UnholdMemory(bufferPtr, transferSize);
  412.         if ((vmHoldMask & kHoldCommandBlock) != 0)
  413.             (void) UnholdMemory((Ptr) scsiCommand, cmdBlockLength);
  414.         if ((vmHoldMask & kHoldStack) != 0)
  415.             (void) UnholdMemory(vmProtectedStackBase, kSCSIProtectedStackSize);
  416.         if ((vmHoldMask & kHoldFunction) != 0)
  417.             (void) UnholdMemory(OriginalSCSI, vmFunctionSize);
  418.         /*
  419.          * Return the number of bytes transferred to the caller. If the caller
  420.          * supplied an actual count and the count is no greater than the maximum,
  421.          * ignore any phase errors.
  422.          */
  423.         if (actualTransferCount != NULL) {
  424.             *actualTransferCount = myTransferCount;
  425.             if (status == scPhaseErr
  426.              && writeToDevice == FALSE
  427.              && myTransferCount <= transferSize
  428.              && myTransferCount > 0)
  429.                 status = noErr;
  430.         }
  431.         /*
  432.          * Return an artificial error if the device returns a non-zero status:
  433.          *    statusErr        Caller should issue RequestSense.
  434.          *    controlErr        Device is busy (self-test?) try again later.
  435.          *    ioErr            Something is dreadfully wrong.
  436.          *    scPhaseErr        This may not be a "real" error -- it may mean that the
  437.          *                    user data buffer was too large for the transfer. (See
  438.          *                    the check above.)
  439.          * Also, there is a bug in the combination of System 7.0.1 and the 53C96
  440.          * that may cause the real SCSI Status Byte to be in the Message byte.
  441.          */
  442.         if (*stsBytePtr == kScsiStatusGood
  443.          && messageByte == kScsiStatusCheckCondition)
  444.             *stsBytePtr = kScsiStatusCheckCondition;
  445.         if (status == noErr) {
  446.             switch (*stsBytePtr) {
  447.             case kScsiStatusGood:                                    break;
  448.             case kScsiStatusCheckCondition:    status = statusErr;        break;
  449.             case kScsiStatusBusy:            status = controlErr;    break;
  450.             default:                        status = ioErr;            break;
  451.             }
  452.         }
  453.         return (status);
  454. }
  455.  
  456. static void NextFunction(void) { }    /* Dummy function for OriginalSCSI size    */
  457.  
  458. static Boolean
  459. IsVirtualMemoryRunning(void)
  460. {
  461.         OSErr                        status;
  462.         long                        response;
  463.         
  464.         status = Gestalt(gestaltVMAttr, &response);
  465.         /*
  466.          * VM is active iff Gestalt succeeded and the response is appropriate.
  467.          */
  468.         return (status == noErr && ((response & (1 << gestaltVMPresent)) != 0));
  469. }
  470.